#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;

namespace Grawlix.Geometry
{
	/// <summary>
	/// An immutable 2D geometric shape, closed or unclosed.
	/// </summary>
	/// <remarks>A shape needs to be something you can draw without
	/// removing the pen from the paper.</remarks>
	public abstract class Shape
	{
		/// <summary>
		/// Finds the area of the shape, or 0 if not closed.
		/// </summary>
		public abstract float Area { get; }

		/// <summary>
		/// Finds an axis-aligned bounding box for the shape.
		/// </summary>
		public virtual Rect BoundingBox
		{
			get { return OrientedBoundingBox.BoundingBox; }
		}

		/// <summary>
		/// The centroid of the shape.
		/// </summary>
		/// <remarks>The centroid of a shape is its center of mass,
		/// given uniform density.</remarks>
		public abstract Vector2 Centroid { get; }

		/// <summary>
		/// Finds an oriented (non-axis-aligned) bounding box for the
		/// shape.
		/// </summary>
		public abstract OrientedRect OrientedBoundingBox { get; }

		/// <summary>
		/// Checks to see if a point is within the
		/// current <see cref="Shape"/>.
		/// </summary>
		/// <param name="point">The point to check.</param>
		/// <returns>true if within, else false.</returns>
		public abstract bool Contains(Vector2 point);

		/// <summary>
		/// Checks to see if a point is within
		/// this <see cref="Shape"/> after applying a transform.
		/// </summary>
		/// <param name="transform">
		/// The transform to apply to this shape.</param>
		/// <param name="point">The point to check.</param>
		/// <returns>true if within, else false.</returns>
		public bool Contains(Transform2 transform, Vector2 point)
		{
			return Contains(transform.ApplyInverse(point));
		}

		/// <summary>
		/// Finds an axis-aligned bounding box for the 
		/// <see cref="Shape"/> when swept between two transforms.
		/// </summary>
		/// <param name="origin">
		/// The transform at the beginning of the sweep.
		/// </param>
		/// <param name="destination">
		/// The transform at the end of the sweep.
		/// </param>
		/// <returns>The axis-aligned bounding box.</returns>
		/// <remarks>
		/// The result is not necessarily optimal, but a close
		/// approximation for most shapes.
		/// </remarks>
		public virtual Rect SweepBoundingBox(Transform2 origin,
		                                     Transform2
		                                     	destination)
		{
			Rect a = TransformBoundingBox(origin);
			Rect b = TransformBoundingBox(destination);
			return new Rect(Math.Min(a.Left, b.Left),
			                Math.Min(a.Bottom, b.Bottom),
			                Math.Max(a.Right, b.Right),
			                Math.Max(a.Top, b.Top));
		}

		/// <summary>
		/// Finds an axis-aligned bounding box
		/// for the shape after applying a <see cref="Transform2"/>.
		/// </summary>
		/// <param name="transform">The transform to apply.</param>
		/// <returns>The axis-aligned bounding box.</returns>
		public virtual Rect TransformBoundingBox(
			Transform2 transform)
		{
			return
				TransformOrientedBoundingBox(transform).BoundingBox;
		}

		/// <summary>
		/// Finds the centroid of the shape after applying a transform.
		/// </summary>
		/// <param name="transform">The transform to apply to the shape.
		/// </param>
		/// <returns>The centroid of the shape.</returns>
		/// <remarks>The centroid of a shape is its center of mass,
		/// given uniform density.</remarks>
		public Vector2 TransformCentroid(Transform2 transform)
		{
			return transform.Apply(Centroid);
		}

		/// <summary>
		/// Finds an axis-aligned bounding box
		/// for the shape after applying a <see cref="Transform2"/>.
		/// </summary>
		/// <param name="transform">The transform to apply.</param>
		/// <returns>The axis-aligned bounding box.</returns>
		public virtual OrientedRect TransformOrientedBoundingBox(
			Transform2 transform)
		{
			return OrientedBoundingBox.Transform(transform);
		}
	}
}